﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace TrabalhoCG_PHONG
{
    class Renderizador
    {
        private Camera camera;

        private Superficie superficie;

        private Iluminacao iluminacao;

        private Ambiente ambiente;

        private ElemMatrizImagem[,] matrizImagem;

        private double[,] matrizRotacao;

        // Inicia os valores de maior e menor para a normalização dos pixels.
        private double maiorRGB = 1;
        private double menorRGB = 0;

        // constantes maximo e minimo de cinza
        private double cinzaMin = 0;
        private double cinzaMax = 255;

        public Renderizador(Camera c, Superficie s, Iluminacao i, Ambiente a)
        {
            this.camera = c;
            this.superficie = s;
            this.iluminacao = i;
            this.ambiente = a;
        }

        private void inicializaMatrizImagem()
        {
            this.matrizImagem = new ElemMatrizImagem[this.camera.Largura, this.camera.Altura];

            for (int i = 0; i < this.camera.Largura; i++)
            {
                for (int j = 0; j < this.camera.Altura; j++)
                {
                    this.matrizImagem[i, j] = new ElemMatrizImagem(0, 0, 0);
                }
            }
        }

        private void inicializaMatrizRotacao()
        {
            this.camera.RotacaoX = (this.camera.RotacaoX * Math.PI) / 180.0;
            this.camera.RotacaoY = (this.camera.RotacaoY * Math.PI) / 180.0;
            this.camera.RotacaoZ = (this.camera.RotacaoZ * Math.PI) / 180.0;
        
            // Obtém o valor dos cossenos.
            double cosRotacaoX = Math.Cos(this.camera.RotacaoX);
            double cosRotacaoY = Math.Cos(this.camera.RotacaoY);
            double cosRotacaoZ = Math.Cos(this.camera.RotacaoZ);
        
            // Obtém o valor dos senos.
            double senRotacaoX = Math.Sin(this.camera.RotacaoX);
            double senRotacaoY = Math.Sin(this.camera.RotacaoY);
            double senRotacaoZ = Math.Sin(this.camera.RotacaoZ);
        
            // Inicialização da matriz rotação.
            this.matrizRotacao = new double[3, 3];
        
            // Linha 1.
            this.matrizRotacao[0, 0] = (cosRotacaoY * cosRotacaoZ);
            this.matrizRotacao[0, 1] = (cosRotacaoY * senRotacaoZ);
            this.matrizRotacao[0, 2] = (- senRotacaoY);
        
            // Linha 2.
            this.matrizRotacao[1, 0] = ((cosRotacaoZ * senRotacaoX * senRotacaoY) - (cosRotacaoX * senRotacaoZ));
            this.matrizRotacao[1, 1] = ((senRotacaoX * senRotacaoY * senRotacaoZ) + (cosRotacaoX * cosRotacaoZ));
            this.matrizRotacao[1, 2] = (cosRotacaoY * senRotacaoX);
        
            // Linha 3.
            this.matrizRotacao[2, 0] = ((cosRotacaoX * senRotacaoY * cosRotacaoZ) + (senRotacaoX * senRotacaoZ));
            this.matrizRotacao[2, 1] = ((cosRotacaoX * senRotacaoY * senRotacaoZ) - (senRotacaoX * cosRotacaoZ));
            this.matrizRotacao[2, 2] = (cosRotacaoX * cosRotacaoY);
        }

        /**
         * Método que calcula o denominador dos valores de Z.
         * 
         * @param a
         * @param b
         * @return double - denominador.
         */
        private double calculaDenominador(double a, double b)
        {
            return ((2.0 * this.superficie.F * a)
                    + (this.superficie.B * Math.Pow(b, 2))
                    + (this.superficie.C)
                    + (2.0 * this.superficie.E * b)
                    + (2.0 * this.superficie.D * a * b)
                    + (this.superficie.A * Math.Pow(a, 2)));
        }

        /**
         * Método que calcula o produto escalar de dois arrays.
         * 
         * @param array1
         * @param array2
         * @return double - produto escalar.
         */
        private double calculaProdutoEscalar(double[] array1, double[] array2)
        {
            return ((array1[0] * array2[0])
                    + (array1[1] * array2[1])
                    + (array1[2] * array2[2]));
        }

        /**
         * Método que calcula o módulo de um array.
         * 
         * @param array
         * @return double - módulo.
         */
        private double calculaModulo(double[] array)
        {
            return (Math.Sqrt((array[0] * array[0])
                    + (array[1] * array[1])
                    + (array[2] * array[2])));
        }

        /**
         * Método de normalização.
         * 
         * @param i
         * @param j
         * @return valor normalizado.
         */
        private int[] normaliza(int i, int j)
        {
            double a = (this.maiorRGB - this.menorRGB);
            a = (a <= 0.0) ? 1 : (this.cinzaMax - this.cinzaMin) / a;
            double b = this.cinzaMin - (this.menorRGB * a);

            int red = (int)(this.matrizImagem[i, j].R * a + b);
            int green = (int)(this.matrizImagem[i, j].G * a + b);
            int blue = (int)(this.matrizImagem[i, j].B * a + b);

            return (new int[] {(red < 0) ? 0 : ((red > 255) ? 255 : red),
                            (green < 0) ? 0 : ((green > 255) ? 255 : green),
                            (blue < 0) ? 0 : ((blue > 255) ? 255 : blue)});
        }


        /**
         * Método que calcula o cosseno através de dois arrays.
         * 
         * @param array1
         * @param array2
         * @return cosseno.
         */
        private double calculaCosseno(double[] array1, double[] array2)
        {
            double produtoEscalar = calculaProdutoEscalar(array1, array2);
            if (produtoEscalar < 0.0)
            {
                return 0.0;
            }

            double modulo = (calculaModulo(array1) * calculaModulo(array2));
            if (modulo == 0.0)
            {
                return 0.0;
            }

            double resultado = (produtoEscalar / modulo);
            if (resultado < 0.0)
            {
                return 0.0;
            }
            else
            {
                return resultado;
            }
        }

        /**
         * Método que calcula o numerador da parte 1 dos valores de Z.
         * 
         * @param a
         * @param b
         * @return double
         */
        private double calculaNumerador1(double a, double b)
        {
            return ((-2.0 * this.superficie.D * this.camera.PosicaoX * b)
                    - (2.0 * this.superficie.B * this.camera.PosicaoY * b)
                    - (2.0 * this.superficie.J)
                    + (2.0 * this.superficie.A * Math.Pow(a, 2) * this.camera.PosicaoZ)
                    - (2.0 * this.superficie.A * camera.PosicaoX * a)
                    - (2.0 * this.superficie.H * b)
                    + (2.0 * this.superficie.F * a * this.camera.PosicaoZ)
                    - (2.0 * this.superficie.E * this.camera.PosicaoY)
                    - (2.0 * this.superficie.G * a)
                    + (2.0 * this.superficie.E * b * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.B * Math.Pow(b, 2) * this.camera.PosicaoZ)
                    - (2.0 * this.superficie.F * this.camera.PosicaoX)
                    - (2.0 * this.superficie.D * a * this.camera.PosicaoY))
                    + (4.0 * this.superficie.D * a * b * this.camera.PosicaoZ);
        }

        /**
         * Método que calcula o numerador da parte 2.0 dos valores de Z.
         * 
         * @param a
         * @param b
         * @return double - numerador parte 2.
         */
        private double calculaNumerador2(double a, double b)
        {
            double resultado = (-(2.0 * this.superficie.C * this.superficie.G * this.camera.PosicaoX)
                    + (Math.Pow(this.superficie.D, 2.0) * Math.Pow(this.camera.PosicaoX, 2.0) * Math.Pow(b, 2.0))
                    + (2.0 * this.superficie.J * this.superficie.H * b)
                    - (2.0 * this.superficie.E * b * this.superficie.K)
                    + (Math.Pow(this.superficie.J, 2.0))
                    - (this.superficie.C * this.superficie.K)
                    + (Math.Pow(this.superficie.F, 2.0) * Math.Pow(a, 2.0) * Math.Pow(this.camera.PosicaoZ, 2.0))
                    + (Math.Pow(this.superficie.E, 2.0) * Math.Pow(b, 2.0) * Math.Pow(this.camera.PosicaoZ, 2.0))
                    + (Math.Pow(this.superficie.D, 2.0) * Math.Pow(a, 2.0) * Math.Pow(this.camera.PosicaoY, 2.0))
                    + (2.0 * this.superficie.J * this.superficie.E * this.camera.PosicaoY)
                    + (2.0 * this.superficie.J * this.superficie.G * a)
                    + (2.0 * this.superficie.J * this.superficie.F * this.camera.PosicaoX)
                    - (2.0 * this.superficie.F * a * this.superficie.K)
                    - (this.superficie.B * Math.Pow(b, 2.0) * this.superficie.K)
                    - (2.0 * this.superficie.C * this.superficie.H * this.camera.PosicaoY)
                    - (this.superficie.C * this.superficie.A * Math.Pow(this.camera.PosicaoX, 2.0))
                    - (this.superficie.C * this.superficie.B * Math.Pow(this.camera.PosicaoY, 2.0))
                    - (this.superficie.A * Math.Pow(a, 2.0) * this.superficie.K)
                    + (2.0 * this.superficie.D * this.camera.PosicaoX * Math.Pow(b, 2.0) * this.superficie.H)
                    - (2.0 * this.superficie.D * this.camera.PosicaoX * b * this.superficie.F * a * this.camera.PosicaoZ)
                    - (2.0 * this.superficie.D * this.camera.PosicaoX * b * this.superficie.E * this.camera.PosicaoY)
                    - (2.0 * this.superficie.D * this.camera.PosicaoX * b * this.superficie.G * a)
                    + (2.0 * this.superficie.D * this.camera.PosicaoX * Math.Pow(b, 2.0) * this.superficie.E * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.D * Math.Pow(this.camera.PosicaoX, 2.0) * b * this.superficie.F)
                    - (2.0 * Math.Pow(this.superficie.D, 2.0) * this.camera.PosicaoX * b * a * this.camera.PosicaoY)
                    + (2.0 * this.superficie.B * this.camera.PosicaoY * b * this.superficie.A * this.camera.PosicaoX * a)
                    + (2.0 * this.superficie.B * this.camera.PosicaoY * b * this.superficie.F * a * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.B * this.camera.PosicaoY * b * this.superficie.G * a)
                    + (2.0 * this.superficie.B * this.camera.PosicaoY * b * this.superficie.F * this.camera.PosicaoX)
                    + (Math.Pow(this.superficie.H, 2.0) * Math.Pow(b, 2.0))
                    + (Math.Pow(this.superficie.E, 2.0) * Math.Pow(this.camera.PosicaoY, 2.0))
                    + (Math.Pow(this.superficie.G, 2.0) * Math.Pow(a, 2.0))
                    + (this.superficie.F * 2.0 * Math.Pow(this.camera.PosicaoX, 2.0))
                    - (2.0 * this.superficie.A * Math.Pow(a, 2.0) * this.camera.PosicaoZ * this.superficie.E * this.camera.PosicaoY)
                    + (2.0 * this.superficie.A * this.camera.PosicaoX * a * this.superficie.H * b)
                    + (2.0 * this.superficie.A * this.camera.PosicaoX * a * this.superficie.E * this.camera.PosicaoY)
                    + (2.0 * this.superficie.A * this.camera.PosicaoX * a * this.superficie.E * b * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.H * b * this.superficie.F * a * this.camera.PosicaoZ)
                    - (2.0 * this.superficie.H * b * this.superficie.E * this.camera.PosicaoY)
                    + (2.0 * this.superficie.H * b * this.superficie.G * a)
                    + (2.0 * this.superficie.H * Math.Pow(b, 2.0) * this.superficie.E * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.H * b * this.superficie.F * this.camera.PosicaoX)
                    - (2.0 * this.superficie.H * b * this.superficie.D * a * this.camera.PosicaoY)
                    - (2.0 * this.superficie.F * a * this.camera.PosicaoZ * this.superficie.E * this.camera.PosicaoY)
                    + (2.0 * this.superficie.F * Math.Pow(a, 2.0) * this.camera.PosicaoZ * this.superficie.G)
                    + (2.0 * this.superficie.F * a * Math.Pow(this.camera.PosicaoZ, 2.0) * this.superficie.E * b)
                    - (2.0 * Math.Pow(this.superficie.F, 2.0) * a * this.camera.PosicaoZ * this.camera.PosicaoX)
                    + (2.0 * this.superficie.F * Math.Pow(a, 2.0) * this.camera.PosicaoZ * this.superficie.D * this.camera.PosicaoY)
                    + (2.0 * this.superficie.E * this.camera.PosicaoY * this.superficie.G * a)
                    - (2.0 * Math.Pow(this.superficie.E, 2.0) * this.camera.PosicaoY * b * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.E * this.camera.PosicaoY * this.superficie.F * this.camera.PosicaoX)
                    + (2.0 * this.superficie.E * Math.Pow(this.camera.PosicaoY, 2.0) * this.superficie.D * a)
                    - (2.0 * this.superficie.E * this.camera.PosicaoY * this.superficie.D * a * b * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.G * a * this.superficie.E * b * this.camera.PosicaoZ)
                    - (2.0 * this.superficie.G * a * this.superficie.F * this.camera.PosicaoX)
                    + (2.0 * this.superficie.G * Math.Pow(a, 2.0) * this.superficie.D * this.camera.PosicaoY)
                    - (2.0 * this.superficie.E * b * this.camera.PosicaoZ * this.superficie.F * this.camera.PosicaoX)
                    - (2.0 * this.superficie.B * Math.Pow(b, 2.0) * this.camera.PosicaoZ * this.superficie.F * this.camera.PosicaoX)
                    - (2.0 * this.superficie.F * this.camera.PosicaoX * this.superficie.D * a * this.camera.PosicaoY)
                    + (2.0 * this.superficie.D * this.camera.PosicaoX * b * this.superficie.J)
                    + (2.0 * this.superficie.B * this.camera.PosicaoY * b * this.superficie.J)
                    - (2.0 * this.superficie.J * this.superficie.A * Math.Pow(a, 2.0) * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.J * this.superficie.A * this.camera.PosicaoX * a)
                    - (2.0 * this.superficie.J * this.superficie.F * a * this.camera.PosicaoZ)
                    - (2.0 * this.superficie.J * this.superficie.E * b * this.camera.PosicaoZ)
                    - (2.0 * this.superficie.J * this.superficie.B * Math.Pow(b, 2.0) * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.J * this.superficie.D * a * this.camera.PosicaoY)
                    - (4.0 * this.superficie.J * this.superficie.D * a * b * this.camera.PosicaoZ)
                    - (4.0 * this.superficie.F * a * this.superficie.H * this.camera.PosicaoY)
                    - (2.0 * this.superficie.F * a * this.superficie.B * Math.Pow(this.camera.PosicaoY, 2.0))
                    - (2.0 * this.superficie.B * Math.Pow(b, 2.0) * this.superficie.G * this.camera.PosicaoX)
                    - (this.superficie.B * Math.Pow(b, 2.0) * this.superficie.A * Math.Pow(this.camera.PosicaoX, 2.0))
                    - (4.0 * this.superficie.E * b * this.superficie.G * this.camera.PosicaoX)
                    - (2.0 * this.superficie.E * b * this.superficie.A * Math.Pow(this.camera.PosicaoX, 2.0))
                    - (2.0 * this.superficie.A * Math.Pow(a, 2.0) * this.superficie.H * this.camera.PosicaoY)
                    - (this.superficie.A * Math.Pow(a, 2.0) * this.superficie.B * Math.Pow(this.camera.PosicaoY, 2.0))
                    - (2.0 * this.superficie.C * this.superficie.D * this.camera.PosicaoX * this.camera.PosicaoY)
                    - (this.superficie.C * this.superficie.B * Math.Pow(b, 2.0) * Math.Pow(this.camera.PosicaoZ, 2.0))
                    - (this.superficie.C * this.superficie.A * Math.Pow(a, 2.0) * Math.Pow(this.camera.PosicaoZ, 2.0))
                    + (2.0 * this.superficie.C * this.superficie.A * this.camera.PosicaoX * a * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.C * this.superficie.H * b * this.camera.PosicaoZ)
                    - (2.0 * this.superficie.C * this.superficie.D * a * Math.Pow(this.camera.PosicaoZ, 2.0) * b)
                    + (2.0 * this.superficie.C * this.superficie.B * this.camera.PosicaoY * b * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.C * this.superficie.D * this.camera.PosicaoX * b * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.C * this.superficie.G * a * this.camera.PosicaoZ)
                    + (2.0 * this.superficie.C * this.superficie.D * a * this.camera.PosicaoZ * this.camera.PosicaoY)
                    - (2.0 * this.superficie.D * a * b * this.superficie.K));

            // Verificar se o retorno for negativo.
            return (resultado >= 0.0) ? Math.Sqrt(resultado) : -1.0;
        }

        /**
         * Método que executa o algoritmo de Phong.
         */
        private void algoritmoPhonG()
        {
            // Variáveis de conversão do sistema de tela para o sistema espaço-imagem.
            double meiaLargura = (this.camera.Largura / 2.0);
            double meiaAltura = (this.camera.Altura / 2.0);
            double lambdaX = (1.0 / this.camera.PixelX);
            double lambdaY = (1.0 / this.camera.PixelY);

            for (int i = 0; i < this.camera.Largura; i++)
            {
                for (int j = 0; j < this.camera.Altura; j++)
                {
                    // Conversão do sistema de tela para o sistema espaço-imagem.
                    double x = (lambdaX * (i - meiaLargura));
                    double y = -(lambdaY * (j - meiaAltura));

                    // Cálculo dos valores de A e B 
                    double a = (this.matrizRotacao[0, 0] * x)
                            + (this.matrizRotacao[1, 0] * y)
                            + (this.matrizRotacao[2, 0] * this.camera.DistanciaFocal);
                    a = a / ((this.matrizRotacao[0, 2] * x)
                            + (this.matrizRotacao[1, 2] * y)
                            + (this.matrizRotacao[2, 2] * this.camera.DistanciaFocal));

                    double b = (this.matrizRotacao[0, 1] * x)
                            + (this.matrizRotacao[1, 1] * y)
                            + (this.matrizRotacao[2, 1] * this.camera.DistanciaFocal);
                    b = b / ((this.matrizRotacao[0, 2] * x)
                            + (this.matrizRotacao[1, 2] * y)
                            + (this.matrizRotacao[2, 2] * this.camera.DistanciaFocal));

                    // Cálculo de z1 e z2
                    double denominadorZ = calculaDenominador(a, b);

                    if (denominadorZ != 0.0)
                    {
                        double numerador = Math.Sqrt(calculaNumerador2(a, b));

                        if (numerador > -1.0)
                        {
                            double z1 = calculaNumerador1(a, b);
                            double z2 = z1;

                            double mulNumerador = (2.0 * numerador);

                            z1 = (0.5 * (z1 + mulNumerador)) / denominadorZ;
                            z2 = (0.5 * (z1 - mulNumerador)) / denominadorZ;

                            // Cálculo da interseção do raio oriundo da câmera com a superfície dada.
                            double intersecaoX1 = this.camera.PosicaoX + (z1 - this.camera.PosicaoZ) * a;
                            double intersecaoY1 = this.camera.PosicaoY + (z1 - this.camera.PosicaoZ) * b;

                            double intersecaoX2 = this.camera.PosicaoX + (z1 - this.camera.PosicaoZ) * a;
                            double intersecaoY2 = this.camera.PosicaoY + (z1 - this.camera.PosicaoZ) * b;

                            // Cálculo da distância entre o CP e o ponto (x1, y1, z1) ou (x2, y2, z1).
                            double distancia1 = Math.Sqrt(Math.Pow(this.camera.PosicaoX - intersecaoX1, 2.0) + Math.Pow(this.camera.PosicaoY - intersecaoY1, 2.0) + Math.Pow(this.camera.PosicaoZ - z1, 2.0));
                            double distancia2 = Math.Sqrt(Math.Pow(this.camera.PosicaoX - intersecaoX2, 2.0) + Math.Pow(this.camera.PosicaoY - intersecaoY2, 2.0) + Math.Pow(this.camera.PosicaoZ - z2, 2.0));

                            // print intersecaoX1, intersecaoY1, distancia1, a, b.

                            double[] vetor = new double[3];

                            // Decide sobre qual o menor.
                            if (distancia1 < distancia2)
                            {
                                vetor[0] = intersecaoX1;
                                vetor[1] = intersecaoY1;
                                vetor[2] = z1;
                            }
                            else
                            {
                                vetor[0] = intersecaoX2;
                                vetor[1] = intersecaoY2;
                                vetor[2] = z2;
                            }

                            // Calcula vetor Normal.
                            double[] vetorNormal = new double[3];

                            vetorNormal[0] = (2.0 * this.superficie.A * vetor[0])
                                    + (2.0 * this.superficie.D * vetor[1])
                                    + (2.0 * this.superficie.F * vetor[2])
                                    + (2.0 * this.superficie.G);
                            vetorNormal[1] = (2.0 * this.superficie.B * vetor[1])
                                    + (2.0 * this.superficie.D * vetor[0])
                                    + (2.0 * this.superficie.E * vetor[2])
                                    + (2.0 * this.superficie.H);
                            vetorNormal[2] = (2.0 * this.superficie.C * vetor[2])
                                    + (2.0 * this.superficie.E * vetor[1])
                                    + (2.0 * this.superficie.F * vetor[0])
                                    + (2.0 * this.superficie.J);

                            // Calcula vetor Direcao da Fonte de Luz.
                            double[] vetorFonteLuz = new double[3];
                            vetorFonteLuz[0] = (this.iluminacao.PosicaoX - vetor[0]);
                            vetorFonteLuz[1] = (this.iluminacao.PosicaoY - vetor[1]);
                            vetorFonteLuz[2] = (this.iluminacao.PosicaoZ - vetor[2]);

                            // Calcula vetor Direcao do Observador.
                            double[] vetorObservador = new double[3];
                            vetorObservador[0] = (this.camera.PosicaoX - vetor[0]);
                            vetorObservador[1] = (this.camera.PosicaoY - vetor[1]);
                            vetorObservador[2] = (this.camera.PosicaoZ - vetor[2]);

                            // Calcula vetor Reflexão.
                            double produtoEscalar = calculaProdutoEscalar(vetorNormal, vetorFonteLuz);

                            double[] vetorReflexao = new double[3];
                            vetorReflexao[0] = (2.0 * produtoEscalar * vetorNormal[0] - vetorFonteLuz[0]);
                            vetorReflexao[1] = (2.0 * produtoEscalar * vetorNormal[1] - vetorFonteLuz[1]);
                            vetorReflexao[2] = (2.0 * produtoEscalar * vetorNormal[2] - vetorFonteLuz[2]);

                            // Calculo do angulo teta.
                            double cossenoTeta = calculaCosseno(vetorFonteLuz, vetorNormal);

                            // Calculo do angulo alfa.
                            double cossenoAlfa = calculaCosseno(vetorReflexao, vetorObservador);

                            // Calcula o ângulo elevado necessário para o calculo da iluminação.
                            double anguloElevado = Math.Pow(cossenoAlfa, this.iluminacao.ExpoenteEspecular);

                            // Aplicando o modelo de Iluminação de Phong para a banda R, G e B.
                            this.matrizImagem[i, j].R = (this.ambiente.R * this.iluminacao.AmbienteR)
                                    + (this.iluminacao.CorLuzR)
                                    * (this.iluminacao.DifusaoR * cossenoTeta)
                                    + (this.iluminacao.EspecularR * anguloElevado);

                            this.matrizImagem[i, j].G = (this.ambiente.G * this.iluminacao.AmbienteG)
                                    + (this.iluminacao.CorLuzG)
                                    * (this.iluminacao.DifusaoG * cossenoTeta)
                                    + (this.iluminacao.EspecularG * anguloElevado);

                            this.matrizImagem[i, j].B = (this.ambiente.B * this.iluminacao.AmbienteB)
                                    + (this.iluminacao.CorLuzB)
                                    * (this.iluminacao.DifusaoB * cossenoTeta)
                                    + (this.iluminacao.EspecularB * anguloElevado);

                            this.menorRGB = Math.Min(Math.Min(this.matrizImagem[i, j].R, this.matrizImagem[i, j].G), Math.Min(this.matrizImagem[i, j].B, this.menorRGB));
                            this.maiorRGB = Math.Max(Math.Max(this.matrizImagem[i, j].R, this.matrizImagem[i, j].G), Math.Max(this.matrizImagem[i, j].B, this.maiorRGB));
                        }
                        // Caso numerador parte 2.0 < 0.
                        else
                        {
                            this.matrizImagem[i, j].R = 0.0;
                            this.matrizImagem[i, j].G = 0.0;
                            this.matrizImagem[i, j].B = 0.0;
                        }
                    }
                    // Caso divisor == 0.
                    else
                    {
                        this.matrizImagem[i, j].R = 0.0;
                        this.matrizImagem[i, j].G = 0.0;
                        this.matrizImagem[i, j].B = 0.0;
                    }
                }
            }
        }

        /**
         * Método responsável por criar a imagem através da chamada dos métodos
         * declarados anteriormente.
         */
        public Bitmap renderizar()
        {
            // Inicialização da matriz imagem.
            inicializaMatrizImagem();

            // Inicialização da matriz rotação.
            inicializaMatrizRotacao();

            // Chamada ao algoritmo de Phong.
            algoritmoPhonG();

            int[] vetorRGB;

            // Cria a imagem gerada através do algoritmo de phong.
            Bitmap imagem = new Bitmap(this.camera.Largura, this.camera.Altura);
            for (int i = 0; i < this.camera.Largura; i++)
            {
                for (int j = 0; j < this.camera.Altura; j++)
                {
                    vetorRGB = normaliza(i, j);
                    Color cor = Color.FromArgb(vetorRGB[0], vetorRGB[1], vetorRGB[2]);
                    imagem.SetPixel(i, j, cor);
                }
            }

            return imagem;
        }
    }
}
